﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using RexToy;
using RexToy.Logging;
using RexToy.UI.WinForm.Pmlx;

using MGAS.Model;
using MGAS.Model.Entity;

namespace MGAS.UI.Controls
{
    public class AlarmPanel : System.Windows.Forms.UserControl, ITemplateItem
    {
        private static ILog _log = LogContext.GetLogger<AlarmPanel>();

        private const string NAME_ATTR = "@Name";
        private const string FLOOR_ATTR = "@FloorNumber";
        private const string BIND_ID_ATTR = "@BindId";

        public event EventHandler<FloorStatusChangedEventArgs> FloorStatusChanged;
        public event EventHandler<EventArgs> BackButtonClicked;

        public string BackText { get; set; }
        public int SensorHeight { get; set; }


        public void SwitchToLocation(Location loc)
        {
            foreach (var grp in _flrGrps)
            {
                Location l = (Location)grp.Tag;
                grp.Visible = (l.Id == loc.Id);
            }
        }

        private List<System.Windows.Forms.GroupBox> _flrGrps;
        private Dictionary<byte, Sensor> _sensors;
        public void ParseTemplate(PmlxNode template)
        {
            _sensors = new Dictionary<byte, Sensor>();

            _flrGrps = new List<System.Windows.Forms.GroupBox>();
            var locs = AllocMap.Instance().FindAllLocations();

            foreach (var loc in locs)
            {
                var b = AllocMap.Instance().FindBuildingByLocation(loc);
                var g = new System.Windows.Forms.GroupBox();
                _flrGrps.Add(g);
                this.Controls.Add(g);

                g.Tag = loc;
                g.Text = b.Text + loc.Text;
                g.Dock = System.Windows.Forms.DockStyle.Fill;

                foreach (var sx in AllocMap.Instance().FindSensorByLocation(loc))
                {
                    Sensor s = new Sensor();

                    byte id = sx.Id;
                    s.AddressId = id;
                    s.PipeName = sx.Text;

                    s.Dock = System.Windows.Forms.DockStyle.Top;
                    s.Height = this.SensorHeight;

                    _sensors.Add(id, s);

                    g.Controls.Add(s);
                }

                var backBtn = new System.Windows.Forms.Button();
                backBtn.Dock = System.Windows.Forms.DockStyle.Bottom;
                backBtn.Height = 40;//TODO:Refactor, adjust layout/dock right?
                backBtn.Text = this.BackText;
                backBtn.Click += new EventHandler(backBtn_Click);
                g.Controls.Add(backBtn);
            }
        }

        void backBtn_Click(object sender, EventArgs e)
        {
            if (BackButtonClicked != null)
            {
                BackButtonClicked(this, e);
            }
        }

        private ISensorDataBlock _blk;
        private TaskScheduler _uiScheduler;
        public void SetDataSource(ISensorDataBlock blk)
        {
            Assertion.IsNull(_blk, "SensorDataBlock is already set.");
            ExceptionBase.Throw_IfArgumentNull(blk, "blk");

            _blk = blk;
            _blk.SensorDataChanged += new EventHandler<SensorDataChangedEventArgs>(_blk_SensorDataChanged);

            _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        }

        void _blk_SensorDataChanged(object sender, SensorDataChangedEventArgs e)
        {
            Task t = new Task(() =>
                {
                    if (this.IsDisposed || this.Disposing)
                        return;

                    _log.Verbose("Updating sensor [id={0}].", e.Id);

                    var s = _sensors[e.Id];                    
                    var data = _blk.GetData(e.Id);
                    if (data == null)
                    {
                        _log.Warning("Can not get data for [id={0}], is the system reset?", e.Id);
                        return;
                    }

                    s.UpdateData(data.Value, data.Status, data.Unit);

                    _log.Verbose("Finish pdate sensor [id={0}].", e.Id);

                    if (FloorStatusChanged != null)
                    {
                        var args = GetFloorChangedEventArg(s);
                        FloorStatusChanged(this, args);
                    }
                }
            );

            t.Start(_uiScheduler);
        }

        private FloorStatusChangedEventArgs GetFloorChangedEventArg(Sensor s)
        {
            var args = new FloorStatusChangedEventArgs();
            foreach (var grp in _flrGrps)
            {
                if (grp.Controls.Contains(s))
                {
                    foreach (var ctrl in grp.Controls)
                    {
                        Sensor sr = ctrl as Sensor;
                        if (sr != null)
                        {
                            if (sr.Status != PressureStatus.Normal)
                                args.Alarm = true;
                        }
                    }
                    args.Location = (Location)grp.Tag;

                    break;
                }
            }
            return args;
        }
    }
}
